/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package engine;

import dbo.Deployment;
import dbo.Equipment;
import dbo.Faction;
import dbo.Player;
import dbo.Special;
import dbo.Squad;
import dbo.Team;
import dbo.Weapon;
import java.util.Arrays;
import java.util.Random;
import java.util.Vector;
import xml.XMLController;

/**
 *
 * @author bi
 */
public final class Game {

    private Vector<Player> vPlayer;
    private Vector<Faction> vFaction;
    private Vector<Squad> vSquad;
    private Vector<Deployment> vDeployment;
    private Vector<Equipment> vEquipment;
    private Vector<Weapon> vWeapon;
    private Vector<Special> vSpecial;
    private String resultText;
    private Random r;

    public Game(XMLController docPlayer, XMLController docFaction,
            XMLController docSquad, XMLController docDeployment,
            XMLController docWeapon, XMLController docEquipment,
            XMLController docSpecial) {
        resultText = "";
        r = new Random(System.currentTimeMillis());

        vPlayer = new Vector<Player>();
        vSquad = new Vector<Squad>();
        vFaction = new Vector<Faction>();
        vDeployment = new Vector<Deployment>();
        vWeapon = new Vector<Weapon>();
        vEquipment = new Vector<Equipment>();
        vSpecial = new Vector<Special>();

        //To know which one load first, consider the ER diagram
        loadVectorFaction(docFaction);
        loadVectorPlayer(docPlayer);
        loadVectorWeapon(docWeapon);
        loadVectorSquad(docSquad);
        loadVectorEquipment(docEquipment);
        loadVectorDeployment(docDeployment);
        loadVectorSpecial(docSpecial);
    }

    // <editor-fold defaultstate="collapsed" desc="Phase 1">
    public boolean startDeploy(String[][] command) {
        try {
            resultText = "--PHASE 1: DEPLOYMENT--\n";
            int maxID = 0;
            for (int i = 0; i < vDeployment.size(); i++) {
                if (vDeployment.get(i).getId() > maxID) {
                    maxID = vDeployment.get(i).getId();
                }
            }

            maxID++;

            for (int i = 0; i < command.length; i++) {
                Player pDeploy = vPlayer.get(vPlayer.indexOf(new Player(command[i][0])));
                Squad sDeploy = vSquad.get(vSquad.indexOf(new Squad(command[i][1])));
                int amount = Integer.valueOf(command[i][2]);
                if (startDeployPlayer(maxID, pDeploy, sDeploy, amount)) {
                    maxID += amount;
                }
            }

            for (int i = 0; i < vPlayer.size(); i++) {
                Player p = vPlayer.get(i);
                if (p.getController().equals(Constant.CTRL_AI_PASSIVE)
                        || p.getController().equals(Constant.CTRL_AI_ACTIVE)) {
                    ArtificialIntelligent computer = new ArtificialIntelligent(p, vDeployment, vSquad);
                    Squad purchaseSquad = computer.makePurchaseDecision();
                    if (purchaseSquad != null) {
                        if (startDeployPlayer(maxID, p, purchaseSquad, 1)) {
                            maxID++;
                        }
                    }
                }
            }
            return true;
        } catch (Exception ex) {
            return false;
        }
    }

    public boolean startDeployPlayer(int id, Player pDeploy, Squad sDeploy, int amount) {
        float remainRes = pDeploy.getRequisition() - sDeploy.getCost() * amount;
        boolean result = false;
        if (remainRes >= 0) {
            resultText += "Player " + pDeploy.getName()
                    + " deploys " + amount
                    + " x " + sDeploy.getName()
                    + " squad(s).\n";

            resultText += "Requisition: " + (int) pDeploy.getRequisition()
                    + " - " + amount
                    + " x " + (int) sDeploy.getCost()
                    + " = " + (int) remainRes
                    + "\n";
            pDeploy.setRequisition(remainRes);
            int curId = id;
            for (int i = 0; i < amount; i++) {
                Deployment d = new Deployment(curId, pDeploy, sDeploy);
                vDeployment.add(d);
                curId++;
            }
            result = true;
        } else {
            resultText += "Player " + pDeploy.getName()
                    + " attempt to deploys " + amount
                    + " " + sDeploy.getName()
                    + " but don't have enough requisition.\n";
        }
        resultText += "\n";
        return result;
    }
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="Phase 2">
    public void startSpecial(String[][] commandList) {
        resultText = "--PHASE 2: BEGIN DAY--\n";
        if (commandList.length <= 0) {
            resultText += "No special attack is ordered.\n\n";
        } else {
            for (int i = 0; i < commandList.length; i++) {
                Special special = vSpecial.get(vSpecial.indexOf(new Special(commandList[i][1])));
                Player player = vPlayer.get(vPlayer.indexOf(new Player(commandList[i][0])));
                float newReq = player.getRequisition() - special.getCost();
                if (newReq >= 0) {
                    player.setRequisition(newReq);
                    try {
                        Player pTargeted = getFactionTarget(commandList[i][2], false);
                        switch (r.nextInt(3)) {
                            case 1:
                                resultText += "Commander " + commandList[i][0];
                                break;
                            case 2:
                                resultText += "General " + commandList[i][0];
                                break;
                            default:
                                resultText += "Player " + commandList[i][0];
                                break;
                        }
                        switch (r.nextInt(3)) {
                            case 1:
                                resultText += " commands " + special.squad.getName();
                                break;
                            case 2:
                                resultText += " indicate " + special.squad.getName();
                                break;
                            default:
                                resultText += " orders " + special.squad.getName();
                                break;
                        }
                        switch (r.nextInt(3)) {
                            case 1:
                                resultText += " to activate " + commandList[i][1] + " at ";
                                break;
                            case 2:
                                resultText += " to begin " + commandList[i][1] + " on ";
                                break;
                            default:
                                resultText += " to use " + commandList[i][1] + " against ";
                                break;
                        }
                        resultText += pTargeted.getName() + ".\n";

                        startSpecialPlayer(special, pTargeted);
                        resultText += "\n";
                    } catch (IndexOutOfBoundsException ex) {
                        resultText += "No player could be found.\n";
                    }
                } else {
                    resultText += "Player " + commandList[i][0] + " don't have enough requisition to activate.\n\n";
                }
            }
        }
    }

    private void startSpecialPlayer(Special special, Player targetedPlayer) {
        for (int i = 0; i < vDeployment.size(); i++) {
            Deployment dTargeted = vDeployment.get(i);
            if (dTargeted.player.equals(targetedPlayer)) {
                if (special.getBonus().equals("Health")) {
                    double bonus = 0;
                    if (special.isPercent()) {
                        bonus = dTargeted.getHealthTotal() * ((double) special.getAmount() / 100);
                    } else {
                        bonus = special.getAmount();
                    }
                    bonus = Math.floor(bonus);
                    if (bonus < 0) {
                        bonus = Math.max(bonus, -dTargeted.getHealthTotal());
                    }
                    int afterHealth = dTargeted.getHealthTotal() + (int) bonus;
                    int afterSize = (int) Math.ceil((double) afterHealth / dTargeted.squad.getHealth());
                    int sizeGap = dTargeted.getSizeTotal() - afterSize;
                    resultText += "- " + dTargeted.squad.getName() + ": " + (int) bonus + "hp(" + sizeGap + " unit).\n";
                    dTargeted.setHealthTotal(afterHealth);
                    dTargeted.setSizeTotal(afterSize);

                    if (dTargeted.getHealthTotal() <= 0) {
                        switch (r.nextInt(4)) {
                            case 3:
                                resultText += dTargeted.squad.getName() + " squad is reported KIA.\n";
                                break;
                            case 2:
                                resultText += dTargeted.squad.getName() + " squad is perished.\n";
                                break;
                            case 1:
                                resultText += dTargeted.squad.getName() + " squad is destroyed.\n";
                                break;
                            default:
                                resultText += dTargeted.squad.getName() + " squad is out of action.\n";
                                break;
                        }
                        vDeployment.remove(dTargeted);
                        i--;
                    }
                }
            }
        }
    }// </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="Phase 3">
    public boolean startCombat(String[][] command, String bonus) {
        resultText = "--PHASE 3: COMBAT--\n";
        if (!bonus.equals("Clear Day")) {
            resultText += bonus + " occurs throughout planet today\n.";
        }

        Vector<Player> vRemain = new Vector<Player>(vPlayer);
        for (int i = 0; i < command.length; i++) {
            Player pAttacker = vPlayer.get(vPlayer.indexOf(new Player(command[i][0])));
            Player pDefender = null;
            if (!command[i][1].equals(Constant.COMMAND_RANDOM)) {
                pDefender = getFactionTarget(command[i][1], false);
            }
            boolean result = false;
            while (!result) {
                if (pDefender == null) {
                    pDefender = getFactionTarget(pAttacker.faction.getTeam(), true);
                    if (pDefender == null) {
                        return false;
                    }
                }
                result = startCombatPlayer(pAttacker, pDefender, bonus);
                if (!result) {
                    pDefender = null;
                }
            }

            if (vRemain.contains(pAttacker)) {
                vRemain.remove(pAttacker);
            }
        }

        //All player must participate, no exception
        for (int i = 0; i < vRemain.size(); i++) {
            Player pAttacker = vRemain.get(i);
            if (!pAttacker.getController().equals(Constant.CTRL_AI_PASSIVE)) {
                Player pDefender = null;
                boolean result = false;
                while (!result) {
                    if (pDefender == null) {
                        pDefender = getFactionTarget(pAttacker.faction.getTeam(), true);
                        if (pDefender == null) {
                            return false;
                        }
                    }
                    result = startCombatPlayer(pAttacker, pDefender, bonus);
                    if (!result) {
                        pDefender = null;
                    }
                }
            }
        }

        return true;
    }

    private boolean startCombatPlayer(Player pAttacker, Player pDefender, String bonus) {
        if (pAttacker.getController().equals(Constant.CTRL_HUMAN)) {
            switch (r.nextInt(3)) {
                case 2:
                    resultText += "**Player " + pAttacker.getName();
                    break;
                case 1:
                    resultText += "**Commander " + pAttacker.getName();
                    break;
                default:
                    resultText += "**Soldiers of " + pAttacker.getName();
                    break;
            }
        } else {
            resultText += "**Computer " + pAttacker.getName();
        }
        switch (r.nextInt(3)) {
            case 2:
                resultText += " approaches " + pDefender.getName() + ".\n";
                break;
            case 1:
                resultText += " assaults on " + pDefender.getName() + ".\n";
                break;
            default:
                resultText += " attempts to overthrow " + pDefender.getName() + ".\n";
                break;
        }
        String aBonus = "";
        String dBonus = "";
        //Get all attacker, defender unit to a vector
        Vector<Deployment> vAttacker = new Vector<Deployment>();
        Vector<Deployment> vDefender = new Vector<Deployment>();
        Vector<Deployment> vStealth = new Vector<Deployment>();
        for (int i = 0; i < vDeployment.size(); i++) {
            Deployment p = vDeployment.get(i);
            if (p.player.equals(pAttacker)) {
                if (!isSquadHasSkill(p.squad, "Passive")) {
                    vAttacker.add(p);
                }
                if (isSquadHasSkill(p.squad, "Artillery")) {
                    aBonus = "Artillery";
                } else if (isSquadHasSkill(p.squad, "Synapse")) {
                    aBonus = "Synapse";
                }
            } else if (p.player.equals(pDefender)) {
                if (isSquadHasSkill(p.squad, "Stealth")
                        && !bonus.equals("Rain Storm")) {
                    vStealth.add(p);
                } else {
                    vDefender.add(p);
                }
                if (isSquadHasSkill(p.squad, "Synapse")) {
                    dBonus = "Synapse";
                }
            }
        }

        //Synapse could be overwritten by this
        if (!bonus.equals("Clear Day")) {
            aBonus = bonus;
            dBonus = bonus;
        }

        boolean flagOK = true;

        if (vDefender.isEmpty() && vStealth.isEmpty()) {
            resultText += "No target could be found for " + pDefender.getName() + ".\n";
            flagOK = false;
        } else {
            for (int i = 0; i < vAttacker.size(); i++) {
                //Target decide which group should be attacked.
                boolean result;
                if (!vAttacker.isEmpty()) {
                    Deployment dAttacker = vAttacker.get(i);
                    int defIdx = -1;
                    Deployment dDefender = new Deployment();
                    int target = 0;
                    if (isSquadHasSkill(dAttacker.squad, "Deep Strike")) {
                        if (!vStealth.isEmpty()) {
                            target = 2;
                        } else {
                            target = 1;
                        }
                    } else {
                        if (!vDefender.isEmpty()) {
                            target = 1;
                        } else {
                            target = 2;
                        }
                    }
                    if (target == 1) {
                        defIdx = r.nextInt(vDefender.size());
                        dDefender = vDefender.get(defIdx);
                    } else if (target == 2) {
                        defIdx = r.nextInt(vStealth.size());
                        dDefender = vStealth.get(defIdx);
                    }

                    switch (r.nextInt(3)) {
                        case 2:
                            resultText += pAttacker.getName() + " orders " + dAttacker.squad.getName();
                            break;
                        case 1:
                            resultText += pAttacker.getName() + " uses " + dAttacker.squad.getName();
                            break;
                        default:
                            resultText += pAttacker.getName() + " deploys " + dAttacker.squad.getName();
                            break;
                    }
                    switch (r.nextInt(3)) {
                        case 5:
                            resultText += " and battles " + pDefender.getName() + "'s " + dDefender.squad.getName();
                            break;
                        case 4:
                            resultText += ", hopes to crush " + pDefender.getName() + "'s " + dDefender.squad.getName();
                            break;
                        case 2:
                            resultText += " to strike at " + pDefender.getName() + "'s " + dDefender.squad.getName();
                            break;
                        case 1:
                            resultText += " against " + pDefender.getName() + "'s " + dDefender.squad.getName();
                            break;
                        default:
                            resultText += " to engage with " + pDefender.getName() + "'s " + dDefender.squad.getName();
                            break;
                    }
                    resultText += "\n";
                    //Who strike first?
                    boolean isPassive = (isSquadHasSkill(dAttacker.squad, "Melee")
                            && !isSquadHasSkill(dAttacker.squad, "Jet Pack")
                            && !isSquadHasSkill(dDefender.squad, "Melee"))
                            || (isSquadHasSkill(dDefender.squad, "First Strike")
                            && !isSquadHasSkill(dAttacker.squad, "First Strike"));
                    //Can Kiting?
                    boolean isAtkKiting = (isSquadHasSkill(dAttacker.squad, "Kiting")
                            || (isSquadHasSkill(dAttacker.squad, "Anti Walker")
                            && isSquadHasSkill(dDefender.squad, "Walker")))
                            && !isSquadHasSkill(dDefender.squad, "Kiting");
                    boolean isDefKiting = (isSquadHasSkill(dDefender.squad, "Kiting")
                            || (isSquadHasSkill(dDefender.squad, "Anti Walker")
                            && isSquadHasSkill(dAttacker.squad, "Walker")))
                            && !isSquadHasSkill(dAttacker.squad, "Kiting");
                    //Battle Commence
                    boolean dDeath = false;
                    boolean aDeath = false;
                    //Lost initiative
                    if (isPassive && !bonus.equals("Rain Storm")) {
                        if (isAtkKiting) {
                            result = false;
                        } else {
                            result = startCombatDeployment(dDefender, dAttacker, dBonus);
                            if (isSquadHasSkill(dDefender.squad, "Suicide")) {
                                dDeath = true;
                            }
                        }
                        if (result) {
                            aDeath = true;
                        } else if (!dDeath) {
                            if (isDefKiting) {
                                result = false;
                            } else {
                                result = startCombatDeployment(dAttacker, dDefender, aBonus);
                                if (isSquadHasSkill(dAttacker.squad, "Suicide")) {
                                    aDeath = true;
                                }
                            }
                            if (result) {
                                dDeath = true;
                            }
                        }
                        //Gain initiative
                    } else {
                        if (isDefKiting) {
                            result = false;
                        } else {
                            result = startCombatDeployment(dAttacker, dDefender, aBonus);
                            if (isSquadHasSkill(dAttacker.squad, "Suicide")) {
                                aDeath = true;
                            }
                        }
                        if (result) {
                            dDeath = true;
                        } else {
                            if (isSquadHasSkill(dAttacker.squad, "No Counter")) {
                                resultText += "No counter back.\n";
                            } else {
                                if (isAtkKiting) {
                                    result = false;
                                } else {
                                    result = startCombatDeployment(dDefender, dAttacker, dBonus);
                                    if (isSquadHasSkill(dDefender.squad, "Suicide")) {
                                        dDeath = true;
                                    }
                                }
                                if (result) {
                                    aDeath = true;
                                }
                            }
                        }
                    }

                    //Is anyone death?
                    if (dDeath) {
                        if (target == 1) {
                            vDefender.remove(defIdx);
                        } else {
                            vStealth.remove(defIdx);
                        }
                    }
                    if (aDeath) {
                        vAttacker.remove(i);
                        i--;
                    }
                    resultText += "\n";
                }
            }
        }
        resultText += "\n";
        return flagOK;
    }

    private boolean startCombatDeployment(Deployment dAttacker, Deployment dDefender, String bonus) {

        Weapon priWeapon = null;
        Vector<Equipment> secEquipment = new Vector<Equipment>();
        secEquipment.removeAllElements();
        int priCarrier = dAttacker.getSizeTotal();
        int secCarrier = 0;
        for (int i = 0; i < vEquipment.size(); i++) {
            Equipment e = vEquipment.get(i);
            if (e.squad.equals(dAttacker.squad)) {
                if (e.getAmount() == 0) {
                    priWeapon = e.weapon;
                } else {
                    secEquipment.add(e);
                    priCarrier--;
                    secCarrier++;
                }
            }
        }
        if (priCarrier < 0) {
            secCarrier += priCarrier;
            priCarrier = 0;
        }
        int healthDamage = 0;

        resultText += "+ " + dDefender.squad.getName() + ": ";
        //Secondary Weapon
        for (int z = 0; z < secEquipment.size() && secCarrier > 0; z++) {
            Equipment equipment = secEquipment.get(z);
            int secFactCarrier = Math.min(secCarrier, equipment.getAmount());
            Weapon secWeapon = equipment.weapon;
            healthDamage += startCombatWeapon(secWeapon, dAttacker, dDefender, secFactCarrier);
            secCarrier = Math.max(0, secCarrier - equipment.getAmount());
        }
        //Primary Weapon
        if (priWeapon != null && priCarrier > 0) {
            healthDamage += startCombatWeapon(priWeapon, dAttacker, dDefender, priCarrier);
        }

        resultText += "= -" + healthDamage + "hp ";
        boolean useSkill = false;
        if (bonus.equals("Winter Storm")) {
            if (!dAttacker.squad.faction.getName().equals("Imperial Guard")) {
                useSkill = true;
                healthDamage *= 0.8;
                resultText += "x0.8(Winter Storm) ";
            }
        } else if (bonus.equals("Rain Storm")) {
            if (!dAttacker.squad.faction.getTeam().equals("Ork")) {
                useSkill = true;
                healthDamage *= 0.8;
                resultText += "x0.8(Rain Storm) ";
            }
        } else if (bonus.equals("Magnetic Storm")) {
            if (dAttacker.squad.faction.getTeam().equals("Necron")) {
                useSkill = true;
                healthDamage *= 1.2;
                resultText += "x1.2(Magnetic Storm) ";
            }
        } else if (bonus.equals("Warp Storm")) {
            if (isSquadHasSkill(dAttacker.squad, "Psychic")) {
                useSkill = true;
                healthDamage *= 1.5;
                resultText += "x1.5(Warp Storm) ";
            } else if (!dAttacker.squad.faction.getTeam().equals("Chaos")) {
                useSkill = true;
                healthDamage *= 0.8;
                resultText += "x0.8(Warp Storm) ";
            }
        } else if (bonus.equals("Spore Air")) {
            if (dAttacker.squad.faction.getTeam().equals("Tyranid")) {
                useSkill = true;
                healthDamage *= 1.2;
                resultText += "x1.2(Spore Air) ";
            }
        } else if (bonus.equals("Synapse")
                && dAttacker.player.faction.getTeam().equals("Tyranid")) {
            useSkill = true;
            healthDamage *= 1.1;
            resultText += "x1.1(Synapse) ";
        }

        if (isSquadHasSkill(dAttacker.squad, "Blood Lust")) {
            useSkill = true;
            healthDamage *= 1.05;
            resultText += "x1.05(Blood Lust) ";
        }
        if (isSquadHasSkill(dDefender.squad, "Valor")) {
            useSkill = true;
            healthDamage *= 0.95;
            resultText += "x0.95(Valor) ";
        }
        if (isSquadHasSkill(dDefender.squad, "Entrench")
                && !isSquadHasSkill(dAttacker.squad, "Melee")
                && !isSquadHasSkill(dAttacker.squad, "Artillery")) {
            useSkill = true;
            healthDamage *= 0.8;
            resultText += "x0.8(Entrench) ";
        }
        if (isSquadHasSkill(dAttacker.squad, "Mobz")) {
            useSkill = true;
            double mobzPower = Math.round(Math.pow(1.01, dAttacker.squad.getSize()) * 100) / 100;
            healthDamage *= mobzPower;
            resultText += "x" + mobzPower + "(Mobz Bonus) ";
        }
        if (isSquadHasSkill(dAttacker.squad, "Zeal")) {
            useSkill = true;
            double zealPower = Math.round(Math.pow(0.95, (dAttacker.squad.getSize() - 1)) * 100) / 100;
            healthDamage *= zealPower;
            resultText += "x" + zealPower + "(Zeal Defense) ";
        }
        if (isSquadHasSkill(dAttacker.squad, "Anti Psyker")) {
            useSkill = true;
            if (isSquadHasSkill(dDefender.squad, "Psyker")) {
                healthDamage *= 1.2;
                resultText += "x1.2(Anti Psyker) ";
            }
        }
        if (isSquadHasSkill(dAttacker.squad, "Anti Daemon")) {
            useSkill = true;
            if (isSquadHasSkill(dDefender.squad, "Daemon")) {
                healthDamage *= 1.2;
                resultText += "x1.2(Anti Daemon) ";
            }
        }
        if (dAttacker.squad.hasSkill("Jet Pack")) {
            if (isSquadHasSkill(dDefender.squad, "Infantry")
                    || isSquadHasSkill(dDefender.squad, "Psyker")
                    || isSquadHasSkill(dDefender.squad, "Daemon")
                    || isSquadHasSkill(dDefender.squad, "Hero")) {
                useSkill = true;
                healthDamage += dAttacker.squad.getSize();
                resultText += "-" + dAttacker.squad.getSize() + "hp(Jet Pack) ";
            }
        }
        if (bonus.equals("Artillery")) {
            useSkill = true;
            healthDamage += 5;
            resultText += "+5(Artillery Support) ";
        }
        if (useSkill) {
            resultText += "= -" + healthDamage + "hp ";
        }

        healthDamage = Math.min(dDefender.getHealthTotal(), healthDamage);
        int remainHealth = dDefender.getHealthTotal() - healthDamage;
        remainHealth = Math.max(0, remainHealth);

        int sizeDamage = dDefender.getSizeTotal() - (int) Math.ceil((double) remainHealth / dDefender.squad.getHealth());
        sizeDamage = Math.min(dDefender.getSizeTotal(), sizeDamage);
        int remainSize = dDefender.getSizeTotal() - sizeDamage;
        remainSize = Math.max(0, remainSize);
        dDefender.setHealthTotal(remainHealth);
        dDefender.setSizeTotal(remainSize);

        resultText += "(" + sizeDamage + " unit).\n";

        if (isSquadHasSkill(dAttacker.squad, "Suicide")) {
            vDeployment.remove(dAttacker);
            resultText += dDefender.squad.getName() + " is exploded.\n";
        }

        if (remainHealth == 0) {
            boolean isRevived = false;
            if (isSquadHasSkill(dDefender.squad, "Reassemble")) {
                if (r.nextInt(2) == 0) {
                    isRevived = true;
                    dDefender.setSizeTotal(dDefender.squad.getSize() / 2);
                    dDefender.setHealthTotal(dDefender.getSizeTotal() * dDefender.squad.getHealth());
                    resultText += dDefender.squad.getName() + " squad is killed, but revived and now has " + dDefender.getHealthTotal() + "hp.\n";
                    isRevived = true;
                }
            }
            if (!isRevived) {
                switch (r.nextInt(4)) {
                    case 3:
                        resultText += dDefender.squad.getName() + " squad is reported KIA.\n";
                        break;
                    case 2:
                        resultText += dDefender.squad.getName() + " squad is perished.\n";
                        break;
                    case 1:
                        resultText += dDefender.squad.getName() + " squad is destroyed.\n";
                        break;
                    default:
                        resultText += dDefender.squad.getName() + " squad is out of action.\n";
                        break;
                }
                vDeployment.remove(dDefender);
                return true;
            }
        }
        return false;
    }

    public double startCombatWeapon(Weapon w, Deployment a, Deployment d, int weaponAmount) {
        if (w.hasSkill("Melee") && isSquadHasSkill(d.squad, "Aircraft")) {
            return 0;
        }
        int sizeBlast = (Math.min(w.getSpread(), d.squad.getSize()) + 1) / 2;
        int numberAttack = w.getRound() * weaponAmount;
        double localDamage = 0;
        double maximumDamage = numberAttack * sizeBlast * d.squad.getHealth();
        float hitChance = w.getAccuracy();
        hitChance = Math.min(hitChance, 1f);
        hitChance = Math.max(hitChance, 0);
        float penChance = (float) w.getPenetration() / d.squad.getArmor();
        penChance = Math.min(penChance, 1f);
        penChance = Math.max(penChance, 0);

        if (isSquadHasSkill(a.squad, "Looted")) {
            hitChance *= 0.95;
            penChance *= 0.95;
        }
        if (isSquadHasSkill(a.squad, "Anti Vehicle")) {
            if (isSquadVehicle(d.squad) || isSquadHasSkill(d.squad, "Armored")) {
                penChance *= 1.2;
            } else {
                hitChance *= 0.8;
            }
        }
        if (isSquadHasSkill(a.squad, "Anti Infantry")) {
            if (isSquadInfantry(d.squad) && !isSquadHasSkill(d.squad, "Armored")) {
                hitChance *= 1.2;
            } else {
                penChance *= 0.8;
            }
        }
        if (isSquadHasSkill(d.squad, "Fleet")) {
            hitChance *= 0.9;
        }

        for (int i = 0; i < numberAttack; i++) {
            //1st - Hit or not
            if (getSpecialRandom() <= hitChance) {
                //HIT
                //2nd - Penetrate or not
                if (getSpecialRandom() <= penChance) {
                    //PENETRATED - FULL DAMAGE
                    //3rd - Damage to health
                    localDamage += (int) Math.floor((double) w.getDamage() * sizeBlast);
                } else {
                    //BOUNCE - REDUCE DAMAGE
                    localDamage += (int) Math.floor((double) w.getDamage() * sizeBlast * penChance);
                }
            } else {
                //MISS - BLAST ONLY DAMAGE
                localDamage += (int) Math.floor((double) w.getDamage() * (sizeBlast - 1) * penChance);
            }
        }
        int roundLocalDamage = (int) Math.round(localDamage);
        resultText += "-" + roundLocalDamage + "hp(" + w.getName() + " x " + weaponAmount + ") ";
        return Math.min(roundLocalDamage, maximumDamage);
    }
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="Phase 4">
    public void startReport(boolean doSkill, boolean doResource, String bonus) {
        resultText = "--PHASE 4: END DAY--\n";
        if (doResource) {
            resultText += "**RESOURCE\n";
            for (int i = 0; i < vPlayer.size(); i++) {
                float addAmount = 200;
                Player p = vPlayer.get(i);
                if (isPlayerDeploymentEmpty(p) || p.getController().equals(Constant.CTRL_AI_BOSS)) {
                    addAmount = 0;
                }
                for (int j = 0; j < vDeployment.size(); j++) {
                    Deployment d = vDeployment.get(j);
                    if (d.player.equals(p)) {
                        if (d.squad.hasSkill("BuilderC")) {
                            addAmount += 20;
                        }
                        if (d.squad.hasSkill("BuilderB")) {
                            addAmount += 40;
                        }
                        if (d.squad.hasSkill("BuilderA")) {
                            addAmount += 80;
                        }
                    }
                }
                if (bonus.equals("Sand Storm") && !p.faction.getSubname().equals("Space Marine")) {
                    addAmount = 0;
                }
                p.setRequisition(p.getRequisition() + addAmount);
                resultText += "Player " + p.getName() + " gains " + String.valueOf(addAmount) + " requisitions.\n";
            }
        }
        int[] factionPoint = new int[vFaction.size()];
        Arrays.fill(factionPoint, 0);
        if (doSkill) {
            resultText += "**DAY SKILL\n";
        }
        Vector<String> vHealing = new Vector<String>();
        for (int i = 0; i < vDeployment.size(); i++) {
            Deployment d = vDeployment.get(i);
            int idx = vFaction.indexOf(new Faction(d.player.faction.getSubname()));
            if (doSkill) {
                if (d.squad.hasSkill("Continous Reinforce")) {
                    int newHealth = (int) Math.floor((double) d.getHealthTotal() * 1.3);
                    int oldSize = d.getSizeTotal();
                    int newSize = (int) Math.ceil((double) newHealth / d.squad.getHealth());
                    int amountOfGain = newSize - oldSize;
                    if (amountOfGain != 0) {
                        resultText += "Squad " + d.squad.getName() + " of " + d.player.getName() + " gains ";
                        resultText += amountOfGain + " men from Continous Reinforce.\n";
                    }
                    d.setHealthTotal(newHealth);
                    d.setSizeTotal(newSize);
                }
                if (d.squad.hasSkill("Healing")) {
                    int healPoint = 10;
                    String healTeam = d.player.faction.getTeam();
                    if (vHealing.contains(healTeam)) {
                        resultText += healTeam + " healing can be used only once a day.";
                    } else {
                        vHealing.add(healTeam);
                        resultText += "Squad " + d.squad.getName() + " of " + d.player.getName() + " uses Medical Treatment.\n";
                        for (int j = 0; j < vDeployment.size(); j++) {
                            Deployment p = vDeployment.get(j);
                            if (p.player.faction.getTeam().equals(healTeam) && !isSquadVehicle(d.squad)) {
                                int fullHealth = p.getSizeTotal() * p.squad.getHealth();
                                int regainHealth = fullHealth - p.getHealthTotal();
                                regainHealth = Math.min(regainHealth, healPoint);
                                p.setHealthTotal(p.getHealthTotal() + regainHealth);
                                if (regainHealth != 0) {
                                    resultText += "+ " + p.player.getName() + "'s " + p.squad.getName() + " regains " + regainHealth + "hp.\n";
                                }
                            }
                        }
                    }
                }
                if (d.squad.hasSkill("Transport")) {
                    String healTeam = d.player.faction.getTeam();
                    resultText += "Squad " + d.squad.getName() + " of " + d.player.getName() + " is Transporting Reinforcement.\n";
                    for (int j = 0; j < vDeployment.size(); j++) {
                        Deployment p = vDeployment.get(j);
                        if (p.player.faction.getTeam().equals(healTeam) && !isSquadVehicle(d.squad)) {
                            int healPoint = (int) Math.ceil(p.getSizeTotal() * 0.25);
                            int regainSize = p.squad.getSize() - p.getSizeTotal();
                            regainSize = Math.min(regainSize, healPoint);
                            regainSize = Math.max(regainSize, 0);
                            p.setSizeTotal(p.getSizeTotal() + regainSize);
                            p.setHealthTotal(p.getSizeTotal() * p.squad.getHealth());
                            if (regainSize != 0) {
                                resultText += "+ " + p.player.getName() + "'s " + p.squad.getName() + " gains " + regainSize + " unit.\n";
                            }
                        }
                    }
                }
            }
            if (idx == -1) {
                resultText += "ERROR: Unknown Faction Found:" + d.player.faction + "\n";
            } else {
                if (d.getSizeTotal() == d.squad.getSize()) {
                    factionPoint[idx] += vDeployment.get(i).squad.getCost();
                } else {
                    factionPoint[idx] += Math.round(
                            (double) vDeployment.get(i).squad.getCost()
                            / vDeployment.get(i).squad.getSize()
                            * vDeployment.get(i).getSizeTotal());
                }
            }
        }

        resultText += "\n**AREA CONTROL\n";
        Vector<Team> vTeam = new Vector<Team>();
        for (int i = 0; i < vFaction.size(); i++) {
            int idx = vTeam.indexOf(new Team(vFaction.get(i).getTeam(), 0));
            if (idx == -1) {
                Team t = new Team(vFaction.get(i).getTeam(), factionPoint[i]);
                vTeam.add(t);
            } else {
                Team t = vTeam.get(idx);
                t.setPoint(t.getPoint() + factionPoint[i]);
                vTeam.set(idx, t);
            }
        }

        int controlPercent = 0;
        int sum = 0;
        for (int i : factionPoint) {
            sum += i;
        }

        for (int i = 0; i < vTeam.size(); i++) {
            int percentTeam = (int) Math.floor(Double.valueOf(vTeam.get(i).getPoint()) / sum * 100);
            resultText += vTeam.get(i).getName() + " Control: " + percentTeam + "%\n";
            controlPercent += percentTeam;
        }
        int controlArea = (int) Math.floor((double) controlPercent / 100 * sum / 10);
        int conflictArea = (int) Math.floor(sum / 10) - controlArea;
        int totalArea = controlArea + conflictArea;
        resultText += "\n------------\n";
        resultText += "Base Area: " + controlArea + "\n";
        resultText += "Conflict Area: " + conflictArea + "\n";
        resultText += "Total Area: " + totalArea + "\n";
    }
// </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="Phase 5">
    public void startStatus() {
        resultText = "--PHASE 5: STATUS--\n";

        for (int i = 0; i < vPlayer.size(); i++) {
            Player p = vPlayer.get(i);
            resultText += "Player: " + p.getName() + "\n";
            resultText += "Faction: " + p.faction.getName() + "\n";
            resultText += "Part: " + p.faction.getSubname() + "\n";
            resultText += "Requisition: " + p.getRequisition() + "\n";
            resultText += "Current Squad: ";
            boolean hasSquad = false;
            for (int j = 0; j < vDeployment.size(); j++) {
                Deployment d = vDeployment.get(j);
                if (d.player.equals(p)) {
                    resultText += "\n+ " + d.squad.getName() + ": " + d.getSizeTotal();
                    if (d.getSizeTotal() > 1) {
                        resultText += " units";
                    } else {
                        resultText += " unit";
                    }
                    hasSquad = true;
                }
            }
            if (!hasSquad) {
                resultText += "None";
            }
            resultText += "\n\n";
        }
    }// </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="Load Functions">
    public void loadVectorFaction(XMLController docFaction) {
        vFaction.removeAllElements();
        for (int i = 0; i < docFaction.table.length; i++) {
            Faction f = new Faction(docFaction.table[i][0],
                    docFaction.table[i][1],
                    docFaction.table[i][2]);
            vFaction.add(f);
        }
    }

    public void loadVectorSpecial(XMLController docSpecial) {
        vSpecial.removeAllElements();
        for (int i = 0; i < docSpecial.table.length; i++) {
            int idxSquad = vSquad.indexOf(new Squad(docSpecial.table[i][1]));
            Special sp = new Special(docSpecial.table[i][0],
                    vSquad.get(idxSquad),
                    Float.parseFloat(docSpecial.table[i][2]),
                    docSpecial.table[i][3],
                    docSpecial.table[i][4],
                    docSpecial.table[i][5]);
            vSpecial.add(sp);
        }
    }

    public void loadVectorPlayer(XMLController docPlayer) {
        vPlayer.removeAllElements();
        for (int i = 0; i < docPlayer.table.length; i++) {
            int idxFaction = vFaction.indexOf(new Faction(docPlayer.table[i][1]));
            try {
                Player p = new Player(docPlayer.table[i][0],
                        vFaction.get(idxFaction),
                        Float.parseFloat(docPlayer.table[i][2]),
                        docPlayer.table[i][3]);
                vPlayer.add(p);
            } catch (ArrayIndexOutOfBoundsException e) {
                System.out.println("Player Vector");
                System.out.println("Name: " + docPlayer.table[i][0]);
                System.out.println("Faction: " + docPlayer.table[i][1]);
            }
        }
    }

    public void loadVectorSquad(XMLController docSquad) {
        vSquad.removeAllElements();
        for (int i = 0; i < docSquad.table.length; i++) {
            int idxFaction = vFaction.indexOf(new Faction(docSquad.table[i][1]));
            try {
                Squad s = new Squad(docSquad.table[i][0],
                        vFaction.get(idxFaction),
                        Float.parseFloat(docSquad.table[i][2]),
                        Integer.parseInt(docSquad.table[i][3]),
                        Integer.parseInt(docSquad.table[i][4]),
                        Integer.parseInt(docSquad.table[i][5]),
                        docSquad.table[i][6]);
                if (s.hasSkill("Sharpshooter")
                        || s.hasSkill("Artillery")) {
                    s.addSkill("Stealth");
                    s.addSkill("First Strike");
                    s.addSkill("No Counter");
                }

                if (s.hasSkill("BuilderA")
                        || s.hasSkill("BuilderB")
                        || s.hasSkill("BuilderC")) {
                    s.addSkill("Passive");
                }
                vSquad.add(s);
            } catch (ArrayIndexOutOfBoundsException e) {
                System.out.println("Squad Vector");
                System.out.println("Name: " + docSquad.table[i][0]);
                System.out.println("Faction: " + docSquad.table[i][1]);
            }
        }
    }

    public void loadVectorEquipment(XMLController docEquipment) {
        vEquipment.removeAllElements();
        for (int i = 0; i < docEquipment.table.length; i++) {
            int idxSquad = vSquad.indexOf(new Squad(docEquipment.table[i][0]));
            int idxWeapon = vWeapon.indexOf(new Weapon(docEquipment.table[i][1]));
            try {
                Equipment e = new Equipment(vSquad.get(idxSquad),
                        vWeapon.get(idxWeapon),
                        Integer.parseInt(docEquipment.table[i][2]));
                vEquipment.add(e);
            } catch (ArrayIndexOutOfBoundsException e) {
                System.out.println("Equipment Vector");
                System.out.println("Squad: " + docEquipment.table[i][0]);
                System.out.println("Weapon: " + docEquipment.table[i][1]);
            }
        }
    }

    public void loadVectorDeployment(XMLController docDeployment) {
        vDeployment.removeAllElements();
        for (int i = 0; i < docDeployment.table.length; i++) {
            int idxPlayer = vPlayer.indexOf(new Player(docDeployment.table[i][1]));
            int idxSquad = vSquad.indexOf(new Squad(docDeployment.table[i][2]));
            if (idxPlayer != -1 && idxSquad != -1) {
                try {
                    Deployment f = new Deployment(Integer.parseInt(docDeployment.table[i][0]),
                            vPlayer.get(idxPlayer),
                            vSquad.get(idxSquad),
                            Integer.parseInt(docDeployment.table[i][3]),
                            Integer.parseInt(docDeployment.table[i][4]));
                    vDeployment.add(f);
                } catch (ArrayIndexOutOfBoundsException e) {
                    System.out.println("Deployment Vector");
                    System.out.println("Name: " + docDeployment.table[i][0]);
                    System.out.println("Player: " + docDeployment.table[i][1]);
                    System.out.println("Squad: " + docDeployment.table[i][2]);
                }
            }
        }
    }

    private void loadVectorWeapon(XMLController docWeapon) {
        vWeapon.removeAllElements();
        for (int i = 0; i < docWeapon.table.length; i++) {
            Weapon w = new Weapon(docWeapon.table[i][0],
                    Integer.parseInt(docWeapon.table[i][1]),
                    Integer.parseInt(docWeapon.table[i][2]),
                    Float.parseFloat(docWeapon.table[i][3]),
                    Integer.parseInt(docWeapon.table[i][4]),
                    Integer.parseInt(docWeapon.table[i][5]),
                    docWeapon.table[i][6]);
            vWeapon.add(w);
        }
    }
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="Other Functions">
    /**
     * @return the resultText
     */
    public String getResultText() {
        return resultText;
    }

    private float getSpecialRandom() {
        return ((float) r.nextInt(10000) / 10000);
    }

    private boolean isPlayerDeploymentEmpty(Player p) {
        boolean pOk = true;
        for (int k = 0; k < vDeployment.size() && pOk; k++) {
            if (vDeployment.get(k).player.equals(p)) {
                pOk = false;
            }
        }
        return pOk;
    }

    public String[][] getDeploymentResult() {
        String[][] resultDeploy = new String[vDeployment.size()][5];
        for (int i = 0; i < vDeployment.size(); i++) {
            Deployment d = vDeployment.get(i);
            resultDeploy[i][0] = String.valueOf(d.getId());
            resultDeploy[i][1] = d.player.getName();
            resultDeploy[i][2] = d.squad.getName();
            resultDeploy[i][3] = String.valueOf(d.getHealthTotal());
            resultDeploy[i][4] = String.valueOf(d.getSizeTotal());
        }
        return resultDeploy;
    }

    public String[][] getPlayerResult() {
        String[][] resultPlayer = new String[vPlayer.size()][4];
        for (int i = 0; i < vPlayer.size(); i++) {
            Player p = vPlayer.get(i);
            resultPlayer[i][0] = p.getName();
            resultPlayer[i][1] = p.faction.getSubname();
            resultPlayer[i][2] = String.valueOf(p.getRequisition());
            resultPlayer[i][3] = p.getController();
        }
        return resultPlayer;
    }

    /**
     * This function will return player name, depended on player faction. Ticking
     * exception will make the faction be ignored instead of only.<p>
     * Example:
     * <blockquote><pre>
     * getFactionTarget("Chaos",false) return random player in Chaos team.
     * getFactionTarget("Imperium",true) return player NOT in Imperium team.
     * </pre></blockquote>
     * @param targetFaction
     * @param isException
     * @return playerName
     * @throws IndexOutOfBoundsException
     */
    private Player getFactionTarget(String targetFaction, boolean isException) {
        Vector<Player> vRandom = new Vector<Player>();
        if (isException) {
            for (int i = 0; i < vPlayer.size(); i++) {
                Player pTemp = vPlayer.get(i);
                //Player in different team and must not empty
                if (!pTemp.faction.getTeam().equals(targetFaction)) {
                    if (!isPlayerDeploymentEmpty(pTemp)) {
                        vRandom.add(pTemp);
                    }
                }
            }
        } else {
            for (int i = 0; i < vPlayer.size(); i++) {
                Player pTemp = vPlayer.get(i);
                //Player in the targeted team and must not empty
                if (pTemp.faction.getTeam().equals(targetFaction)) {
                    if (!isPlayerDeploymentEmpty(pTemp)) {
                        vRandom.add(pTemp);
                    }
                }
            }
        }
        if (vRandom.isEmpty()) {
            return null;
        } else {
            return vRandom.get(r.nextInt(vRandom.size()));
        }
    }

    private boolean isSquadHasSkill(Squad s, String skillname) {
        boolean hasSkill = s.hasSkill(skillname);
        for (int i = 0; i < vEquipment.size() && !hasSkill; i++) {
            Equipment e = vEquipment.get(i);
            if (e.squad.equals(s)) {
                hasSkill = e.weapon.hasSkill(skillname);
            }
        }
        return hasSkill;
    }

    private boolean isSquadVehicle(Squad s) {
        if (isSquadHasSkill(s, "Vehicle")
                || isSquadHasSkill(s, "Monstrous")
                || isSquadHasSkill(s, "Walker")
                || isSquadHasSkill(s, "Aircraft")) {
            return true;
        }
        return false;
    }

    private boolean isSquadInfantry(Squad s) {
        if (isSquadHasSkill(s, "Infantry")
                    || isSquadHasSkill(s, "Psyker")
                    || isSquadHasSkill(s, "Daemon")
                    || isSquadHasSkill(s, "Armored")) {
            return true;
        }
        return false;
    }
    // </editor-fold>
}
